This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

Install packages

# install.packages("readr")
# install.packages("dplyr")
# install.packages("stringr")
# install.packages("shiny")
# install.packages("ggplot2")
# install.packages("plotly")

Load in packages

# Allows us to read-in csv files
library(readr) 
# For data manipulation
library(dplyr) 

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
# For regular expression operations 
library(stringr) 
# library(shiny)
library(ggplot2)
# Used tp create interactive visualisations
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Load-in dataset

df <- read_csv('Data/GI_age.csv')
Rows: 42 Columns: 7── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): England and Wales Code, England and Wales, Gender identity (7 categories), Age (6 categories)
dbl (3): Gender identity (7 categories) Code, Age (6 categories) Code, Observation
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse of data structure
# But can also click on the dataset in the Environment pane
head(df, 10)
# Let's check out the dimensions

dim(df)
[1] 42  7

Data Cleaning

# str_replace_all() method finds all substrings which match the regex and replaces them with empty string
# First, let's replace any brackets with empty strings
colnames(df) <- str_replace_all(colnames(df), "\\s*\\([^)]*\\)", "")

# Lowercase column text and replace empty spaces with "_"
colnames(df) <- tolower(colnames(df))
colnames(df) <- str_replace_all(colnames(df), " ", "_")

# Let's see if it worked..
head(df)

Pipes and other operators..

So, we’ve already come across the assignment operator ‘<-’ which is used to assign a value. E.g. df <- read_csv(‘Data/GI_age.csv’), here we assign our csv file to a dataframe variable called ‘df’.

But, we’re now going to encounter the pipe operator ‘%>%’ which can seem intimidating at first but is actually pretty simple. It’s used to pass the result of one function directly into the next function. E.g. df <- df %>% filter(gender_identity_code != -8), here we start with our df and pass it to the filter function using the pipe operator. This basically supplies the filter() function with its first argument, which is the dataframe to filter on. And here we encounter a logical operator ‘!=’ within the filter() function, which specifies that we should only keep rows where gender_identity_code is not equal to -8.

# Get rid of columns with 0 observations
df <- df %>% 
  filter(gender_identity_code != -8) 

# Check it worked

head(df, 10)
# Get rid of redundant age category
# Further filter data
df <- df %>%
  filter(age_code != 1)
# Clean up the values in the 'age' column. Let's shorten them.

# Chain str_replace() calls together to apply multiple string replacements in succession
# Each str_replace() call is applied to the result of the previous one
df$age <- df$age %>%
  str_replace('Aged ', '') %>%
  str_replace('to', '-') %>%
  str_replace('years', '') %>%
  str_replace('and over', '+')

# We can pass our df to the select function, where we specify the column we're interested in.
# Then, we pipe the output to the head function.
df %>%
  select(age) %>%
  head()

Question

How is gender identity distributed among different age groups?

Some subquestions that this can help us answer:

Data pre-processing

Calculate percentages

Below, we use the group_by function to group the data by ‘gender_identity’ and calculate the percentage within each group. Then the mutate() function adds a new column ‘percentage’ to df, which (for each group) divides the observation by the sum of observations, multiplies it by 100, and rounds it up to 2 decimal points. We then use the ungroup function when we’re done with the grouping operation.

df <- df %>%
  group_by(gender_identity) %>%
  mutate(percentage = round((observation / sum(observation) * 100), 2)) %>%
  ungroup()

head(df)

Interactive grouped bar chart + stacked bar chart

So, the convention when using Plotly in R, is to create our plot first by using the ggplot2 package. Then, we convert the ggplot object to a ‘plotly’ object using ‘ggplotly’. There’s a lot going on here so I’ll break some of it down. The ggplot() function initialises a ggplot object, which sets up the dataframe that will be used for the plot and specifies the aesthetic mappings which describe how variables in the data are mapped to visual properties. So, inside aes() we specify our x and y columns, and specify that we want to map our age column to fill the colour of the bars.

Meanwhile, geom_bar() is used to make bar charts, so it adds the bar geometry to the plot. And we set stat to ‘identity’, which tells ‘ggplot’ to use the value in the y-axis column (‘percentage’) for the height of the bars. By setting position to ‘dodge’ we ensure that the bars are placed next to each other.

Finally, labs() is used to add or modify labels, and theme is used to customise non-data parts of the plot like text, legend, axes. And scale_fill_discrete() controls the colour scales and here we use the name parameter to label our legend “Age”.

TLDR: we’re using the + operator and ggplot functions to build upon the base ggplot object, layering on aesthetic mappings, geometries, labels, etc.

p <- ggplot(df, aes(x = gender_identity, y = percentage, fill = age,
                    text = paste('Observation:', observation))) +  # Include observation info
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = 'Distribution of Gender Identity Categories Among Age Groups',
       x = 'Gender Identity', y = 'Percentage') +
  theme(plot.title = element_text(hjust = 0.5)) +
  scale_fill_discrete(name = "Age")

# Let's take a look at our static graph
p

Hmm okay. Not too shabby, but we’re definitely going to have to do something about our x-axis labels, as right now everything is pretty cluttered. Maybe we could rotate them, or just rename them. We’ll get round to it. But for now, let’s make this thing interactive.

# Convert ggplot object to a plotly object for interactivity
fig <- ggplotly(p, tooltip = c("y", "fill", "text"))  # Specify tooltip components


# Let's check it out
fig

Tooltips

When using different R libraries geared towards interactive visualisations, you’ll often come across ‘tooltips’. These are small boxes that provide information when a user hovers over a part of a data visualisation such as: a point on a graph, a bar in a bar chart, or a segment in a pie chart. They are used to display additional information about the data point or object, providing more context without cluttering up the chart.

# Set the levels of the factor to the order you want them to appear
df$gender_identity <- factor(df$gender_identity, levels = c(
  "Gender identity the same as sex registered at birth",
  "Gender identity different from sex registered at birth but no specific identity given",
  "Trans woman",
  "Trans man",
  "All other gender identities",
  "Not answered"
))

# Generate the plotly figure
fig <- ggplotly(p)

# Specify custom tick labels with the corresponding tick values
fig <- fig %>%
  layout(
    title = list(text = 'Distribution of Gender Identity Categories Among Age Groups', x = 0.5),
    xaxis = list(
      title = 'Gender Identity',
      tickvals = levels(df$gender_identity),  # Set tickvals to factor levels
      ticktext = c(
        "Cisgender", 
        "Gender identity different from sex",
        "Trans woman",
        "Trans man",
        "All other identities",
        "Not answered"
      )
    ),
    yaxis = list(title = 'Percentage'),
    legend = list(orientation = "v", yanchor = "top", y = -0.3, xanchor = "center", x = 1)
  )

fig

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgojIyBJbnN0YWxsIHBhY2thZ2VzCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQojIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQojIGluc3RhbGwucGFja2FnZXMoInNoaW55IikKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQpgYGAKCiMjIExvYWQgaW4gcGFja2FnZXMKCmBgYHtyfQojIEFsbG93cyB1cyB0byByZWFkLWluIGNzdiBmaWxlcwpsaWJyYXJ5KHJlYWRyKSAKIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShkcGx5cikgCiMgRm9yIHJlZ3VsYXIgZXhwcmVzc2lvbiBvcGVyYXRpb25zIApsaWJyYXJ5KHN0cmluZ3IpIAojIGxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoZ2dwbG90MikKIyBVc2VkIHRwIGNyZWF0ZSBpbnRlcmFjdGl2ZSB2aXN1YWxpc2F0aW9ucwpsaWJyYXJ5KHBsb3RseSkKYGBgCiMjIExvYWQtaW4gZGF0YXNldAoKYGBge3J9CmRmIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2FnZS5jc3YnKQpgYGAKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBvZiBkYXRhIHN0cnVjdHVyZQojIEJ1dCBjYW4gYWxzbyBjbGljayBvbiB0aGUgZGF0YXNldCBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZQpoZWFkKGRmLCAxMCkKYGBgCgpgYGB7cn0KIyBMZXQncyBjaGVjayBvdXQgdGhlIGRpbWVuc2lvbnMKCmRpbShkZikKYGBgCgojIyBEYXRhIENsZWFuaW5nCgpgYGB7cn0KIyBzdHJfcmVwbGFjZV9hbGwoKSBtZXRob2QgZmluZHMgYWxsIHN1YnN0cmluZ3Mgd2hpY2ggbWF0Y2ggdGhlIHJlZ2V4IGFuZCByZXBsYWNlcyB0aGVtIHdpdGggZW1wdHkgc3RyaW5nCiMgRmlyc3QsIGxldCdzIHJlcGxhY2UgYW55IGJyYWNrZXRzIHdpdGggZW1wdHkgc3RyaW5ncwpjb2xuYW1lcyhkZikgPC0gc3RyX3JlcGxhY2VfYWxsKGNvbG5hbWVzKGRmKSwgIlxccypcXChbXildKlxcKSIsICIiKQoKIyBMb3dlcmNhc2UgY29sdW1uIHRleHQgYW5kIHJlcGxhY2UgZW1wdHkgc3BhY2VzIHdpdGggIl8iCmNvbG5hbWVzKGRmKSA8LSB0b2xvd2VyKGNvbG5hbWVzKGRmKSkKY29sbmFtZXMoZGYpIDwtIHN0cl9yZXBsYWNlX2FsbChjb2xuYW1lcyhkZiksICIgIiwgIl8iKQoKIyBMZXQncyBzZWUgaWYgaXQgd29ya2VkLi4KaGVhZChkZikKYGBgCgojIyMgUGlwZXMgYW5kIG90aGVyIG9wZXJhdG9ycy4uCgpTbywgd2UndmUgYWxyZWFkeSBjb21lIGFjcm9zcyB0aGUgYXNzaWdubWVudCBvcGVyYXRvciAnPC0nIHdoaWNoIGlzIHVzZWQgdG8gYXNzaWduIGEgdmFsdWUuIEUuZy4gZGYgPC0gcmVhZF9jc3YoJ0RhdGEvR0lfYWdlLmNzdicpLCBoZXJlIHdlIGFzc2lnbiBvdXIgY3N2IGZpbGUgdG8gYSBkYXRhZnJhbWUgdmFyaWFibGUgY2FsbGVkICdkZicuCgpCdXQsIHdlJ3JlIG5vdyBnb2luZyB0byBlbmNvdW50ZXIgdGhlIHBpcGUgb3BlcmF0b3IgJyU+JScgd2hpY2ggY2FuIHNlZW0gaW50aW1pZGF0aW5nIGF0IGZpcnN0IGJ1dCBpcyBhY3R1YWxseSBwcmV0dHkgc2ltcGxlLiBJdCdzIHVzZWQgdG8gcGFzcyB0aGUgcmVzdWx0IG9mIG9uZSBmdW5jdGlvbiBkaXJlY3RseSBpbnRvIHRoZSBuZXh0IGZ1bmN0aW9uLiBFLmcuIGRmIDwtIGRmICU+JSBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgpLCBoZXJlIHdlIHN0YXJ0IHdpdGggb3VyIGRmIGFuZCBwYXNzIGl0IHRvIHRoZSBmaWx0ZXIgZnVuY3Rpb24gdXNpbmcgdGhlIHBpcGUgb3BlcmF0b3IuIFRoaXMgYmFzaWNhbGx5IHN1cHBsaWVzIHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiB3aXRoIGl0cyBmaXJzdCBhcmd1bWVudCwgd2hpY2ggaXMgdGhlIGRhdGFmcmFtZSB0byBmaWx0ZXIgb24uIEFuZCBoZXJlIHdlIGVuY291bnRlciBhIGxvZ2ljYWwgb3BlcmF0b3IgJyE9JyB3aXRoaW4gdGhlIGZpbHRlcigpIGZ1bmN0aW9uLCB3aGljaCBzcGVjaWZpZXMgdGhhdCB3ZSBzaG91bGQgb25seSBrZWVwIHJvd3Mgd2hlcmUgZ2VuZGVyX2lkZW50aXR5X2NvZGUgaXMgbm90IGVxdWFsIHRvIC04LiAKCmBgYHtyfQojIEdldCByaWQgb2YgY29sdW1ucyB3aXRoIDAgb2JzZXJ2YXRpb25zCmRmIDwtIGRmICU+JSAKICBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgpIAoKIyBDaGVjayBpdCB3b3JrZWQKCmhlYWQoZGYsIDEwKQpgYGAKCmBgYHtyfQojIEdldCByaWQgb2YgcmVkdW5kYW50IGFnZSBjYXRlZ29yeQojIEZ1cnRoZXIgZmlsdGVyIGRhdGEKZGYgPC0gZGYgJT4lCiAgZmlsdGVyKGFnZV9jb2RlICE9IDEpCgpgYGAKCmBgYHtyfQojIENsZWFuIHVwIHRoZSB2YWx1ZXMgaW4gdGhlICdhZ2UnIGNvbHVtbi4gTGV0J3Mgc2hvcnRlbiB0aGVtLgoKIyBDaGFpbiBzdHJfcmVwbGFjZSgpIGNhbGxzIHRvZ2V0aGVyIHRvIGFwcGx5IG11bHRpcGxlIHN0cmluZyByZXBsYWNlbWVudHMgaW4gc3VjY2Vzc2lvbgojIEVhY2ggc3RyX3JlcGxhY2UoKSBjYWxsIGlzIGFwcGxpZWQgdG8gdGhlIHJlc3VsdCBvZiB0aGUgcHJldmlvdXMgb25lCmRmJGFnZSA8LSBkZiRhZ2UgJT4lCiAgc3RyX3JlcGxhY2UoJ0FnZWQgJywgJycpICU+JQogIHN0cl9yZXBsYWNlKCd0bycsICctJykgJT4lCiAgc3RyX3JlcGxhY2UoJ3llYXJzJywgJycpICU+JQogIHN0cl9yZXBsYWNlKCdhbmQgb3ZlcicsICcrJykKCiMgV2UgY2FuIHBhc3Mgb3VyIGRmIHRvIHRoZSBzZWxlY3QgZnVuY3Rpb24sIHdoZXJlIHdlIHNwZWNpZnkgdGhlIGNvbHVtbiB3ZSdyZSBpbnRlcmVzdGVkIGluLgojIFRoZW4sIHdlIHBpcGUgdGhlIG91dHB1dCB0byB0aGUgaGVhZCBmdW5jdGlvbi4KZGYgJT4lCiAgc2VsZWN0KGFnZSkgJT4lCiAgaGVhZCgpCmBgYAoKIyMgUXVlc3Rpb24KCkhvdyBpcyBnZW5kZXIgaWRlbnRpdHkgZGlzdHJpYnV0ZWQgYW1vbmcgZGlmZmVyZW50IGFnZSBncm91cHM/CgpTb21lIHN1YnF1ZXN0aW9ucyB0aGF0IHRoaXMgY2FuIGhlbHAgdXMgYW5zd2VyOgoKKiBXaGF0ICUgb2YgdHJhbnMgbWVuIGFyZSBhZ2VkIDE2LTI0IHllYXJzPwoqIEFyZSBvbGRlciBhZ2UgZ3JvdXBzIG92ZXJyZXByZXNlbnRlZCBpbiB0aGUgJ25vbi1yZXNwb25zZScgY2F0ZWdvcnk/CgojIyBEYXRhIHByZS1wcm9jZXNzaW5nCgojIyMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzIAoKQmVsb3csIHdlIHVzZSB0aGUgZ3JvdXBfYnkgZnVuY3Rpb24gdG8gZ3JvdXAgdGhlIGRhdGEgYnkgJ2dlbmRlcl9pZGVudGl0eScgYW5kIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSB3aXRoaW4gZWFjaCBncm91cC4gVGhlbiB0aGUgbXV0YXRlKCkgZnVuY3Rpb24gYWRkcyBhIG5ldyBjb2x1bW4gJ3BlcmNlbnRhZ2UnIHRvIGRmLCB3aGljaCAoZm9yIGVhY2ggZ3JvdXApIGRpdmlkZXMgdGhlIG9ic2VydmF0aW9uIGJ5IHRoZSBzdW0gb2Ygb2JzZXJ2YXRpb25zLCBtdWx0aXBsaWVzIGl0IGJ5IDEwMCwgYW5kIHJvdW5kcyBpdCB1cCB0byAyIGRlY2ltYWwgcG9pbnRzLiBXZSB0aGVuIHVzZSB0aGUgdW5ncm91cCBmdW5jdGlvbiB3aGVuIHdlJ3JlIGRvbmUgd2l0aCB0aGUgZ3JvdXBpbmcgb3BlcmF0aW9uLiAKCmBgYHtyfQpkZiA8LSBkZiAlPiUKICBncm91cF9ieShnZW5kZXJfaWRlbnRpdHkpICU+JQogIG11dGF0ZShwZXJjZW50YWdlID0gcm91bmQoKG9ic2VydmF0aW9uIC8gc3VtKG9ic2VydmF0aW9uKSAqIDEwMCksIDIpKSAlPiUKICB1bmdyb3VwKCkKCmhlYWQoZGYpCmBgYAoKIyMgSW50ZXJhY3RpdmUgZ3JvdXBlZCBiYXIgY2hhcnQgKyBzdGFja2VkIGJhciBjaGFydAoKU28sIHRoZSBjb252ZW50aW9uIHdoZW4gdXNpbmcgUGxvdGx5IGluIFIsIGlzIHRvIGNyZWF0ZSBvdXIgcGxvdCBmaXJzdCBieSB1c2luZyB0aGUgZ2dwbG90MiBwYWNrYWdlLiBUaGVuLCB3ZSBjb252ZXJ0IHRoZSBnZ3Bsb3Qgb2JqZWN0IHRvIGEgJ3Bsb3RseScgb2JqZWN0IHVzaW5nICdnZ3Bsb3RseScuIFRoZXJlJ3MgYSBsb3QgZ29pbmcgb24gaGVyZSBzbyBJJ2xsIGJyZWFrIHNvbWUgb2YgaXQgZG93bi4gVGhlIGdncGxvdCgpIGZ1bmN0aW9uIGluaXRpYWxpc2VzIGEgZ2dwbG90IG9iamVjdCwgd2hpY2ggc2V0cyB1cCB0aGUgZGF0YWZyYW1lIHRoYXQgd2lsbCBiZSB1c2VkIGZvciB0aGUgcGxvdCBhbmQgc3BlY2lmaWVzIHRoZSBhZXN0aGV0aWMgbWFwcGluZ3Mgd2hpY2ggZGVzY3JpYmUgaG93IHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBhcmUgbWFwcGVkIHRvIHZpc3VhbCBwcm9wZXJ0aWVzLiBTbywgaW5zaWRlIGFlcygpIHdlIHNwZWNpZnkgb3VyIHggYW5kIHkgY29sdW1ucywgYW5kIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIG1hcCBvdXIgYWdlIGNvbHVtbiB0byBmaWxsIHRoZSBjb2xvdXIgb2YgdGhlIGJhcnMuCgpNZWFud2hpbGUsIGdlb21fYmFyKCkgaXMgdXNlZCB0byBtYWtlIGJhciBjaGFydHMsIHNvIGl0IGFkZHMgdGhlIGJhciBnZW9tZXRyeSB0byB0aGUgcGxvdC4gQW5kIHdlIHNldCBzdGF0IHRvICdpZGVudGl0eScsIHdoaWNoIHRlbGxzICdnZ3Bsb3QnIHRvIHVzZSB0aGUgdmFsdWUgaW4gdGhlIHktYXhpcyBjb2x1bW4gKCdwZXJjZW50YWdlJykgZm9yIHRoZSBoZWlnaHQgb2YgdGhlIGJhcnMuIEJ5IHNldHRpbmcgcG9zaXRpb24gdG8gJ2RvZGdlJyB3ZSBlbnN1cmUgdGhhdCB0aGUgYmFycyBhcmUgcGxhY2VkIG5leHQgdG8gZWFjaCBvdGhlci4gCgpGaW5hbGx5LCBsYWJzKCkgaXMgdXNlZCB0byBhZGQgb3IgbW9kaWZ5IGxhYmVscywgYW5kIHRoZW1lIGlzIHVzZWQgdG8gY3VzdG9taXNlIG5vbi1kYXRhIHBhcnRzIG9mIHRoZSBwbG90IGxpa2UgdGV4dCwgbGVnZW5kLCBheGVzLiBBbmQgc2NhbGVfZmlsbF9kaXNjcmV0ZSgpIGNvbnRyb2xzIHRoZSBjb2xvdXIgc2NhbGVzIGFuZCBoZXJlIHdlIHVzZSB0aGUgbmFtZSBwYXJhbWV0ZXIgdG8gbGFiZWwgb3VyIGxlZ2VuZCAiQWdlIi4gCgpUTERSOiB3ZSdyZSB1c2luZyB0aGUgKyBvcGVyYXRvciBhbmQgZ2dwbG90IGZ1bmN0aW9ucyB0byBidWlsZCB1cG9uIHRoZSBiYXNlIGdncGxvdCBvYmplY3QsIGxheWVyaW5nIG9uIGFlc3RoZXRpYyBtYXBwaW5ncywgZ2VvbWV0cmllcywgbGFiZWxzLCBldGMuCgpgYGB7cn0KcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gZ2VuZGVyX2lkZW50aXR5LCB5ID0gcGVyY2VudGFnZSwgZmlsbCA9IGFnZSwKICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUoJ09ic2VydmF0aW9uOicsIG9ic2VydmF0aW9uKSkpICsgICMgSW5jbHVkZSBvYnNlcnZhdGlvbiBpbmZvCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnModGl0bGUgPSAnRGlzdHJpYnV0aW9uIG9mIEdlbmRlciBJZGVudGl0eSBDYXRlZ29yaWVzIEFtb25nIEFnZSBHcm91cHMnLAogICAgICAgeCA9ICdHZW5kZXIgSWRlbnRpdHknLCB5ID0gJ1BlcmNlbnRhZ2UnKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiQWdlIikKCiMgTGV0J3MgdGFrZSBhIGxvb2sgYXQgb3VyIHN0YXRpYyBncmFwaApwCmBgYAoKSG1tIG9rYXkuIE5vdCB0b28gc2hhYmJ5LCBidXQgd2UncmUgZGVmaW5pdGVseSBnb2luZyB0byBoYXZlIHRvIGRvIHNvbWV0aGluZyBhYm91dCBvdXIgeC1heGlzIGxhYmVscywgYXMgcmlnaHQgbm93IGV2ZXJ5dGhpbmcgaXMgcHJldHR5IGNsdXR0ZXJlZC4gTWF5YmUgd2UgY291bGQgcm90YXRlIHRoZW0sIG9yIGp1c3QgcmVuYW1lIHRoZW0uIFdlJ2xsIGdldCByb3VuZCB0byBpdC4gQnV0IGZvciBub3csIGxldCdzIG1ha2UgdGhpcyB0aGluZyBpbnRlcmFjdGl2ZS4KCmBgYHtyfQojIENvbnZlcnQgZ2dwbG90IG9iamVjdCB0byBhIHBsb3RseSBvYmplY3QgZm9yIGludGVyYWN0aXZpdHkKZmlnIDwtIGdncGxvdGx5KHAsIHRvb2x0aXAgPSBjKCJ5IiwgImZpbGwiLCAidGV4dCIpKSAgIyBTcGVjaWZ5IHRvb2x0aXAgY29tcG9uZW50cwoKCiMgTGV0J3MgY2hlY2sgaXQgb3V0CmZpZwpgYGAKCiMjIFRvb2x0aXBzIAoKV2hlbiB1c2luZyBkaWZmZXJlbnQgUiBsaWJyYXJpZXMgZ2VhcmVkIHRvd2FyZHMgaW50ZXJhY3RpdmUgdmlzdWFsaXNhdGlvbnMsIHlvdSdsbCBvZnRlbiBjb21lIGFjcm9zcyAndG9vbHRpcHMnLiBUaGVzZSBhcmUgc21hbGwgYm94ZXMgdGhhdCBwcm92aWRlIGluZm9ybWF0aW9uIHdoZW4gYSB1c2VyIGhvdmVycyBvdmVyIGEgcGFydCBvZiBhIGRhdGEgdmlzdWFsaXNhdGlvbiBzdWNoIGFzOiBhIHBvaW50IG9uIGEgZ3JhcGgsIGEgYmFyIGluIGEgYmFyIGNoYXJ0LCBvciBhIHNlZ21lbnQgaW4gYSBwaWUgY2hhcnQuIFRoZXkgYXJlIHVzZWQgdG8gZGlzcGxheSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkYXRhIHBvaW50IG9yIG9iamVjdCwgcHJvdmlkaW5nIG1vcmUgY29udGV4dCB3aXRob3V0IGNsdXR0ZXJpbmcgdXAgdGhlIGNoYXJ0LiAKCmBgYHtyfQojIFNldCB0aGUgbGV2ZWxzIG9mIHRoZSBmYWN0b3IgdG8gdGhlIG9yZGVyIHlvdSB3YW50IHRoZW0gdG8gYXBwZWFyCmRmJGdlbmRlcl9pZGVudGl0eSA8LSBmYWN0b3IoZGYkZ2VuZGVyX2lkZW50aXR5LCBsZXZlbHMgPSBjKAogICJHZW5kZXIgaWRlbnRpdHkgdGhlIHNhbWUgYXMgc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGgiLAogICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGggYnV0IG5vIHNwZWNpZmljIGlkZW50aXR5IGdpdmVuIiwKICAiVHJhbnMgd29tYW4iLAogICJUcmFucyBtYW4iLAogICJBbGwgb3RoZXIgZ2VuZGVyIGlkZW50aXRpZXMiLAogICJOb3QgYW5zd2VyZWQiCikpCgojIEdlbmVyYXRlIHRoZSBwbG90bHkgZmlndXJlCmZpZyA8LSBnZ3Bsb3RseShwKQoKIyBTcGVjaWZ5IGN1c3RvbSB0aWNrIGxhYmVscyB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIHRpY2sgdmFsdWVzCmZpZyA8LSBmaWcgJT4lCiAgbGF5b3V0KAogICAgdGl0bGUgPSBsaXN0KHRleHQgPSAnRGlzdHJpYnV0aW9uIG9mIEdlbmRlciBJZGVudGl0eSBDYXRlZ29yaWVzIEFtb25nIEFnZSBHcm91cHMnLCB4ID0gMC41KSwKICAgIHhheGlzID0gbGlzdCgKICAgICAgdGl0bGUgPSAnR2VuZGVyIElkZW50aXR5JywKICAgICAgdGlja3ZhbHMgPSBsZXZlbHMoZGYkZ2VuZGVyX2lkZW50aXR5KSwgICMgU2V0IHRpY2t2YWxzIHRvIGZhY3RvciBsZXZlbHMKICAgICAgdGlja3RleHQgPSBjKAogICAgICAgICJDaXNnZW5kZXIiLCAKICAgICAgICAiR2VuZGVyIGlkZW50aXR5IGRpZmZlcmVudCBmcm9tIHNleCIsCiAgICAgICAgIlRyYW5zIHdvbWFuIiwKICAgICAgICAiVHJhbnMgbWFuIiwKICAgICAgICAiQWxsIG90aGVyIGlkZW50aXRpZXMiLAogICAgICAgICJOb3QgYW5zd2VyZWQiCiAgICAgICkKICAgICksCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnUGVyY2VudGFnZScpLAogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICJ2IiwgeWFuY2hvciA9ICJ0b3AiLCB5ID0gLTAuMywgeGFuY2hvciA9ICJjZW50ZXIiLCB4ID0gMSkKICApCgpmaWcKYGBgCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLiAKClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4KCg==